package org.fight.landowner.remoting.protocol;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.fight.landowner.common.constant.CommonConstant;
import org.fight.landowner.common.serialize.SerializableType;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;

/**
 * @Author wangxuehao02
 * @Date 2024/2/6 7:08 PM
 * @Description
 **/
public class RemotingSerializableUtil {
    public static RemotingCommand deSerialize(ByteBuffer byteBuffer) {
        int magic = byteBuffer.getInt();

        if (magic != CommonConstant.REQUEST_MAGIC) {
            throw new RuntimeException();
        }

        RemotingCommandHeader remotingCommandHeader = deSerializationHeader(byteBuffer);

        int bodyLen = byteBuffer.getInt();

        byte[] bodyData = new byte[bodyLen];

        byteBuffer.get(bodyData);

        RemotingCommand remotingCommand = new RemotingCommand();
        remotingCommand.setHeader(remotingCommandHeader);
        remotingCommand.setData(bodyData);

        return remotingCommand;
    }

    public static ByteBuffer serialize(RemotingCommand remotingCommand) {
        if (remotingCommand == null) {
            return null;
        }

        RemotingCommandHeader header = remotingCommand.getHeader();

        ByteBuffer headerByteBuffer = serializationHeader(header);
        int headerByteBufferLen = headerByteBuffer.limit();

        byte[] bodyData = remotingCommand.getData();
        int bodyLen = bodyData.length;

        int len = 4 /*magic*/ + 4 /*headerLen*/ + headerByteBufferLen /*header data*/ + 4 /*bodyLen*/ + bodyLen /*bodyData*/;

        ByteBuffer byteBuffer = ByteBuffer.allocate(4 + len);

        byteBuffer.putInt(len);

        byteBuffer.putInt(CommonConstant.REQUEST_MAGIC);

        byteBuffer.putInt(headerByteBufferLen);

        byteBuffer.put(headerByteBuffer);

        byteBuffer.putInt(bodyLen);

        byteBuffer.put(bodyData);

        byteBuffer.flip();

        return byteBuffer;
    }

    public static ByteBuffer serializationHeader(RemotingCommandHeader header) {
        byte[] extendMessageData = header.getExtendMessage() == null ? null : JSON.toJSONString(header.getExtendMessage()).getBytes(StandardCharsets.UTF_8);;
        int extendMessageDataLen = extendMessageData == null ? 0 : extendMessageData.length;

        // todo 内存怎么释放？
        ByteBuffer byteBuffer = ByteBuffer.allocate(1 + 2 + 2 + 1 + 4 + 1 + extendMessageDataLen);

        byteBuffer.put(header.getType().getCode());

        byteBuffer.putShort(header.getVersion());

        byteBuffer.putShort(header.getCode());

        byteBuffer.put(header.getLanguage().getCode());

        byteBuffer.putInt(header.getRequestId());

        byteBuffer.put(header.getSerializableProtocol().getCode());

        if (extendMessageData != null) {
            byteBuffer.put(extendMessageData);
        }

        byteBuffer.flip();

        return byteBuffer;
    }

    public static RemotingCommandHeader deSerializationHeader(ByteBuffer byteBuffer) {
        int headerLen = byteBuffer.getInt();

        byte type = byteBuffer.get();

        RemotingCommandHeader remotingCommandHeader = RemotingCommandHeader.buildRemotingCommandHeaderByTypeCode(type);

        short version = byteBuffer.getShort();
        short code = byteBuffer.getShort();
        byte language = byteBuffer.get();
        int requestId = byteBuffer.getInt();
        byte serializableProtocol = byteBuffer.get();

        remotingCommandHeader.setVersion(version);
        remotingCommandHeader.setCode(code);
        remotingCommandHeader.setLanguage(LanguageType.getByCode(language));
        remotingCommandHeader.setRequestId(requestId);
        remotingCommandHeader.setSerializableProtocol(SerializableType.getByCode(serializableProtocol));

        headerLen -= 1 + 2 + 2 + 1 + 4 + 1;

        if (headerLen != 0) {
            byte[] extendMessageData = new byte[headerLen];
            byteBuffer.get(extendMessageData);

            HashMap<String, String> extendMessage = JSON.parseObject(new String(extendMessageData, StandardCharsets.UTF_8), new TypeReference<HashMap<String, String>>() {});
            remotingCommandHeader.setExtendMessage(extendMessage);
        }

        return remotingCommandHeader;
    }
}
